/**
 * @file 地图服务核心模块
 * @description 基于单例模式的地图服务模块，负责地图资源加载、状态管理和事件分发
 * @module services/mapService
 * @date 2024-07-25
 * @author GPS项目团队
 * 
 * @description 功能概述:
 * - 管理百度地图API及轨迹插件的动态加载
 * - 提供事件总线机制处理地图相关事件
 * - 维护地图加载状态和错误信息
 * - 检测路由变化并实现真正的按需加载
 * - 提供地图API可用性检测
 * - 实现资源加载重试和错误处理机制
 * 
 * 本服务采用单例模式设计，统一管理整个应用的地图资源，并通过事件机制
 * 向应用其他部分通知地图状态变化。服务设计遵循最小可行产品(MVP)原则，
 * 提供关键功能的同时保持代码简洁。
 * 
 * @requires Vue
 * @requires @/config/map.config
 * @requires @/utils/system/logger
 * @requires @dcloudio/uni-h5
 * 
 * @exports {Object} mapService - 地图服务单例
 * @exports {Function} loadMapOnDemand - 按需加载地图资源
 * @exports {Function} isMapRouteActive - 检查当前路由是否需要地图
 * @exports {Function} isMapAvailable - 检查地图功能是否可用
 */
// @ts-ignore
import { ref, computed } from 'vue';
import { BAIDU_MAP_CONFIG } from '@/config/map.config';
import { logger } from '@/utils/system/logger';
import { getCurrentPages } from '@dcloudio/uni-h5';

// ===== 类型定义 =====
declare global {
  interface Window {
    BMapGL: any;
    Track: any;
    BMap_loadScriptTime: number;
    BMapGL_loadScriptTime: number;
  }
}

// 地图服务状态类型
export type MapLoadStatus = 'unload' | 'loading' | 'loaded' | 'error';

// 地图类型
export type MapType = 'normal' | 'satellite' | 'hybrid';

// 地图功能检测结果
export interface MapCapabilities {
  mapLoaded: boolean;
  trackPluginLoaded: boolean;
}

// 地图事件类型
export type MapEventType = 
  | 'map:loading'     // 地图开始加载
  | 'map:loaded'      // 地图加载成功
  | 'map:error'       // 地图加载错误 
  | 'track:loading'   // 轨迹插件开始加载
  | 'track:loaded'    // 轨迹插件加载成功
  | 'track:error'     // 轨迹插件加载错误
  | 'ready'           // 地图及插件全部就绪
  | 'status-change';  // 状态变更

// 事件回调函数类型
export type MapEventCallback = (data?: any) => void;

// 地图加载配置
export interface MapLoadOptions {
  /** 是否强制重新加载 */
  forceReload?: boolean;
  /** 是否加载轨迹插件 */
  loadTrack?: boolean;
  /** 加载超时时间(毫秒) */
  timeout?: number;
  /** 是否自动重试 */
  retry?: boolean;
  /** 最大重试次数 */
  maxRetries?: number;
}

// 默认地图加载配置
const DEFAULT_MAP_LOAD_OPTIONS: MapLoadOptions = {
  forceReload: false,
  loadTrack: true,
  timeout: 15000,
  retry: true,
  maxRetries: 3
};

// 地图路由配置
const MAP_REQUIRED_ROUTES = ['/pages/monitor', '/monitor', '/track'];

/**
 * 简化的地图服务类
 */
class MapService {
  // 状态管理
  public state = {
    status: ref<MapLoadStatus>('unload'),
    error: ref<string | null>(null),
    capabilities: ref<MapCapabilities>({
      mapLoaded: false,
      trackPluginLoaded: false
    }),
    loading: ref<boolean>(false),
    loadingTrack: ref<boolean>(false),
    lastLoadTime: ref<number | null>(null)
  };

  // 加载控制
  private loadPromise: Promise<boolean> | null = null;
  private trackLoadPromise: Promise<boolean> | null = null;
  
  // 事件总线
  private eventBus: Map<MapEventType, Set<MapEventCallback>> = new Map();

  // 计算属性
  public isMapReady = computed(() => 
    this.state.capabilities.value.mapLoaded && 
    this.state.status.value === 'loaded'
  );

  public isTrackReady = computed(() => 
    this.isMapReady.value && 
    this.state.capabilities.value.trackPluginLoaded
  );

  /**
   * 订阅地图事件
   * @param event 事件类型
   * @param callback 回调函数
   * @returns 取消订阅的函数
   */
  public on(event: MapEventType, callback: MapEventCallback): () => void {
    if (!this.eventBus.has(event)) {
      this.eventBus.set(event, new Set());
    }
    
    this.eventBus.get(event)!.add(callback);
    
    return () => this.off(event, callback);
  }

  /**
   * 取消订阅地图事件
   * @param event 事件类型 
   * @param callback 回调函数
   */
  public off(event: MapEventType, callback: MapEventCallback): void {
    if (this.eventBus.has(event)) {
      this.eventBus.get(event)!.delete(callback);
    }
  }

  /**
   * 触发地图事件
   * @param event 事件类型
   * @param data 事件数据
   */
  private emit(event: MapEventType, data?: any): void {
    if (this.eventBus.has(event)) {
      this.eventBus.get(event)!.forEach(callback => {
        try {
          callback(data);
        } catch (err) {
          logger.error(`地图事件处理错误 [${event}]:`, err);
        }
      });
    }
  }
  
  /**
   * 检查BMapGL是否可用
   * @returns {boolean} 是否可用
   */
  private isBMapGLAvailable(): boolean {
    if (typeof window === 'undefined') return false;

    // 快速检查 BMapGL 对象是否存在
    return typeof window.BMapGL !== 'undefined' && window.BMapGL !== null;
  }
  
  /**
   * 检查Track插件是否可用
   * @returns {boolean} 是否可用
   */
  private isTrackPluginAvailable(): boolean {
    if (typeof window === 'undefined') return false;

    // 检查 Track 对象及其主要功能是否存在
    return typeof window.Track !== 'undefined' && 
           window.Track !== null &&
           typeof window.Track.View === 'function' &&
           typeof window.Track.TrackPoint === 'function';
  }

  /**
   * 检查当前路由是否需要地图
   * @returns {boolean} 当前路由是否需要地图
   */
  public isMapRouteActive(): boolean {
    try {
      // 使用 uni-app 的 getCurrentPages 获取当前页面路径
      let path = '';
      
      // 尝试获取当前页面
      const pages = getCurrentPages();
      if (pages && pages.length > 0) {
        const currentPage = pages[pages.length - 1];
        path = '/' + (currentPage.route || '');
      } else {
        // 回退到使用 window.location.pathname
        path = window.location.pathname || '';
      }
      
      // 检查是否匹配任何需要地图的路由模式
      return MAP_REQUIRED_ROUTES.some(route => {
        if (route.endsWith('*')) {
          // 前缀匹配
          const prefix = route.slice(0, -1);
          return path.startsWith(prefix);
        } else {
          // 精确匹配或包含匹配
          return path.includes(route);
        }
      });
    } catch (err) {
      logger.warn('检查地图路由时出错', err);
      return false;
    }
  }

  /**
   * 检查地图资源状态
   * 统一检查地图API和轨迹插件的可用性，优化性能
   * @param forceUpdate 是否强制更新状态
   * @returns 地图功能可用性状态
   */
  public checkMapResources(forceUpdate: boolean = false): MapCapabilities {
    // 如果不是强制更新且已有检查结果，直接返回缓存值
    if (!forceUpdate && 
        (this.state.capabilities.value.mapLoaded || 
         this.state.capabilities.value.trackPluginLoaded)) {
      return this.state.capabilities.value;
    }
    
    // 检查地图API和轨迹插件
    const mapLoaded = this.isBMapGLAvailable();
    const trackPluginLoaded = this.isTrackPluginAvailable();
    
    // 更新状态
    const newCapabilities = {
      mapLoaded,
      trackPluginLoaded
    };
    
    // 更新全局状态
    this.state.capabilities.value = newCapabilities;
    
    // 如果地图和轨迹插件都已加载，更新整体状态为已加载
    if (mapLoaded && 
        (trackPluginLoaded || !BAIDU_MAP_CONFIG.api.autoLoadPlugins.track)) {
      this.updateStatus('loaded');
    }
    
    logger.debug('检查地图资源状态:', JSON.stringify(newCapabilities));
    return newCapabilities;
  }
  
  /**
   * 统一判断地图功能是否可用
   * 为了避免多处重复检查逻辑，提供一个统一的方法判断地图是否可用
   * @returns {boolean} 地图功能是否完全可用
   */
  public isMapAvailable(): boolean {
    const { mapLoaded, trackPluginLoaded } = this.checkMapResources();
    
    // 如果配置要求加载轨迹插件，则需要两者都可用
    if (BAIDU_MAP_CONFIG.api.autoLoadPlugins.track) {
      return mapLoaded && trackPluginLoaded;
    }
    
    // 否则只需要地图API可用即可
    return mapLoaded;
  }

  /**
   * 初始化地图服务检查
   * @returns {boolean} 初始化是否成功
   */
  public init(): boolean {
    try {
      // 检查BMapGL是否可用
      const mapLoaded = this.isBMapGLAvailable();
      
      // 检查Track插件是否可用
      const trackPluginLoaded = mapLoaded && this.isTrackPluginAvailable();
      
      // 更新状态
      this.state.capabilities.value = { mapLoaded, trackPluginLoaded };
      
      if (mapLoaded) {
        this.updateStatus('loaded');
        
        if (trackPluginLoaded) {
          this.emit('track:loaded');
        }
        
        if (mapLoaded && trackPluginLoaded) {
          this.emit('ready');
        }
        
        return true;
      } else {
        this.updateStatus('error', '地图API未正确加载');
        return false;
      }
    } catch (err) {
      this.updateStatus('error', err instanceof Error ? err.message : '地图初始化失败');
      return false;
    }
  }
  
  /**
   * 更新地图状态
   * @param status 状态
   * @param error 错误信息
   */
  private updateStatus(status: MapLoadStatus, error: string | null = null): void {
    const oldStatus = this.state.status.value;
    
    // 更新状态
    this.state.status.value = status;
    this.state.error.value = error;
    
    // 更新加载状态
    if (status === 'loading') {
      this.state.loading.value = true;
    } else {
      this.state.loading.value = false;
      
      // 记录加载完成时间
      if (status === 'loaded') {
        this.state.lastLoadTime.value = Date.now();
      }
    }
    
    // 触发状态变更事件
    if (oldStatus !== status) {
      this.emit('status-change', { status, error });
      
      // 根据状态触发特定事件
      switch (status) {
        case 'loading':
          this.emit('map:loading');
          break;
        case 'loaded':
          this.emit('map:loaded');
          break;
        case 'error':
          this.emit('map:error', { error });
          break;
      }
    }
  }
  
  /**
   * 按需加载地图资源
   * @param options 加载选项
   * @returns Promise<boolean> 加载是否成功
   */
  public async loadMapOnDemand(options?: MapLoadOptions): Promise<boolean> {
    // 合并默认选项
    const opts: MapLoadOptions = { ...DEFAULT_MAP_LOAD_OPTIONS, ...options };
    
    // 如果已加载且不强制重载，直接返回成功
    if (this.isMapReady.value && !opts.forceReload) {
      logger.info('地图已加载，无需重复加载');
      
      // 如果需要轨迹插件但未加载，则单独加载轨迹插件
      if (opts.loadTrack && !this.state.capabilities.value.trackPluginLoaded) {
        return this.loadTrackPlugin();
      }
      
      return true;
    }
    
    // 如果已经有加载过程在进行中，直接返回现有的Promise
    if (this.loadPromise && this.state.loading.value) {
      logger.info('地图正在加载中，复用现有Promise');
      return this.loadPromise;
    }
    
    // 开始新的加载过程
    this.updateStatus('loading');
    
    // 创建加载Promise
    this.loadPromise = this.loadMapResources(opts);
    
    try {
      const success = await this.loadPromise;
      if (success) {
        this.updateStatus('loaded');
        
        // 如果需要轨迹插件，尝试加载
        if (opts.loadTrack && !this.state.capabilities.value.trackPluginLoaded) {
          await this.loadTrackPlugin();
        }
        
        return true;
      } else {
        throw new Error('加载地图资源失败');
      }
    } catch (err) {
      this.updateStatus('error', err instanceof Error ? err.message : '加载地图资源失败');
      return false;
    } finally {
      this.loadPromise = null;
    }
  }
  
  /**
   * 加载地图核心资源
   */
  private async loadMapResources(opts: MapLoadOptions): Promise<boolean> {
    let retryCount = 0;
    
    while (retryCount <= (opts.maxRetries || 0)) {
      try {
        // 加载主地图API
        const mapLoaded = await this.loadBMapGLScript();
        if (!mapLoaded) {
          throw new Error('加载百度地图API失败');
        }
        
        // 更新能力状态
        this.state.capabilities.value.mapLoaded = true;
        
        return true;
      } catch (err) {
        if (opts.retry && retryCount < (opts.maxRetries || 0)) {
          logger.warn(`加载地图资源失败，尝试重试(${retryCount + 1}/${opts.maxRetries})`, err);
          retryCount++;
          
          // 等待一段时间后重试
          await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
        } else {
          logger.error('加载地图资源失败', err);
          throw err;
        }
      }
    }
    
    return false;
  }
  
  /**
   * 加载轨迹插件
   */
  public async loadTrackPlugin(): Promise<boolean> {
    // 如果地图核心未加载，无法加载插件
    if (!this.state.capabilities.value.mapLoaded) {
      logger.warn('地图核心未加载，无法加载轨迹插件');
      return false;
    }
    
    // 如果轨迹插件已加载，直接返回成功
    if (this.state.capabilities.value.trackPluginLoaded) {
      return true;
    }
    
    // 如果已经有加载过程在进行中，直接返回现有的Promise
    if (this.trackLoadPromise && this.state.loadingTrack.value) {
      logger.info('轨迹插件正在加载中，复用现有Promise');
      return this.trackLoadPromise;
    }
    
    // 开始新的加载过程
    this.state.loadingTrack.value = true;
    this.emit('track:loading');
    
    // 创建加载Promise
    this.trackLoadPromise = this.loadTrackScript();
    
    try {
      const success = await this.trackLoadPromise;
      if (success) {
        this.state.capabilities.value.trackPluginLoaded = true;
        this.emit('track:loaded');
        
        // 如果地图和轨迹插件都加载完成，触发ready事件
        if (this.state.capabilities.value.mapLoaded) {
          this.emit('ready');
        }
      }
      
      return success;
    } catch (err) {
      logger.error('加载轨迹插件失败', err);
      this.emit('track:error', { error: err instanceof Error ? err.message : '加载轨迹插件失败' });
      return false;
    } finally {
      this.state.loadingTrack.value = false;
      this.trackLoadPromise = null;
    }
  }
  
  /**
   * 加载百度地图脚本
   * @returns {Promise<boolean>} 是否加载成功
   */
  private loadBMapGLScript(): Promise<boolean> {
    return new Promise((resolve) => {
      // 如果已经加载，直接返回成功
      if (window.BMapGL) {
        logger.info('百度地图脚本已加载');
        resolve(true);
        return;
      }
      
      logger.info('开始加载百度地图脚本');
      
      // 设置回调函数
      window.onBMapCallback = () => {
        logger.info('百度地图脚本加载完成');
        this.emit('map:loaded');
        resolve(true);
      };
      
      // 创建script标签
      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = `https://api.map.baidu.com/api?type=webgl&v=1.0&ak=${BAIDU_MAP_CONFIG.api.ak}&callback=onBMapCallback`;
      script.onerror = () => {
        const error = '百度地图脚本加载失败';
        logger.error(error);
        this.emit('map:error', { error });
        resolve(false);
      };
      
      document.head.appendChild(script);
    });
  }
  
  /**
   * 加载轨迹插件脚本
   * @returns {Promise<boolean>} 是否加载成功
   */
  private loadTrackScript(): Promise<boolean> {
    return new Promise((resolve) => {
      if (window.Track) {
        logger.info('轨迹插件已加载');
        this.emit('track:loaded');
        resolve(true);
        return;
      }
      
      logger.info('开始加载轨迹插件脚本');
      
      // 加载轨迹插件
      const trackScript = document.createElement('script');
      trackScript.type = 'text/javascript';
      trackScript.src = 'https://unpkg.com/@bmapgl-plugin/track';
      
      trackScript.onload = () => {
        logger.info('轨迹插件加载完成');
        this.emit('track:loaded');
        resolve(true);
      };
      
      trackScript.onerror = () => {
        const error = '轨迹插件加载失败';
        logger.error(error);
        this.emit('track:error', { error });
        resolve(false);
      };
      
      document.head.appendChild(trackScript);
      
      // 加载轨迹JS库
      const trackJsScript = document.createElement('script');
      trackJsScript.type = 'text/javascript';
      trackJsScript.src = 'https://mapopen-pub-jsapi.bj.bcebos.com/jsapiGlgeo/track.js';
      document.head.appendChild(trackJsScript);
    });
  }
}

// 导出单例实例
export const mapService = new MapService(); 